import axios from 'axios'
import qs from 'qs'
import {
  Message
} from 'element-ui'
import requestFilter from './request-filter'
import {
  HTTP_ERROR
} from './error'
import {
  merge
} from 'lodash-es'
import RequestPool from './RequestPool'

export const HIDE_ERROR_PROP = 'hide-error-toast'
export const NO_RESPONSE_INTERCEPTORS = 'no-response-interceptors'

const localurl = window.location.href.split('/')
const basePath = localurl[0] + '//' + localurl[2]

const baseURL = process.env.NODE_ENV === 'production' ? basePath : ''


// 创建axios实例
const service = axios.create({
  baseURL: baseURL,
  headers: {
    'content-type': 'application/json;charset=UTF-8'
  }
})

let errorToatTimer = null

service.interceptors.request.use(
  config => {
    if (!config.data) {
      return config
    }
    const defaultParams = {}
    config[NO_RESPONSE_INTERCEPTORS] = config.headers[NO_RESPONSE_INTERCEPTORS]
    delete config.headers[NO_RESPONSE_INTERCEPTORS]
    if (config.method === 'get') {
      if (config.params) {
        config.params = Object.assign({}, config.params, defaultParams)
      } else {
        config.params = defaultParams
      }
    } else if (config.method === 'post') {
      let data = null
      if (config.data) {
        data = Object.assign({}, config.data, defaultParams)
      } else {
        data = defaultParams
      }

      config[HIDE_ERROR_PROP] = config.headers[HIDE_ERROR_PROP]
      delete config.headers[HIDE_ERROR_PROP]

      if (config.headers['Content-Type'] === 'application/x-www-form-urlencoded') {
        config.data = qs.stringify(data) // 模拟form表单提交时使用qs模块
      } else {
        config.data = data
      }
    }
    return config
  },
  error => {
    Promise.reject(error)
  }
)

service.interceptors.response.use( // 拦截返回response

  response => {

    const res = response.data
    return new Promise((resolve, reject) => {
      requestFilter.run(response, function () {
        if (response.error) {
          if (!response.config[HIDE_ERROR_PROP]) {
            if (!errorToatTimer) {
              Message({
                message: response.error.message,
                type: 'error',
                center: true,
                duration: 2000
              })
              errorToatTimer = setTimeout(() => {
                errorToatTimer && clearTimeout(errorToatTimer)
                errorToatTimer = null
              }, 2000)
            }
          }
          reject(res)
        } else {
          if (response.config[NO_RESPONSE_INTERCEPTORS]) {
            resolve(response)
          } else {
            resolve(res)
          }
        }
      })
    })
  },
  error => {
    if (axios.isCancel(error)) {
      return Promise.reject(new Error(error.message))
    }

    // 网络错误，404等http错误码，与200状态码的返回区别为：
    // 200状态码逻辑：请求正常的情况下，reject的值不是Error类型，而是后台返回的原始数据
    // 其他错误码: 封装后的Error，可判断返回值是否是Error进行区别
    const code = error.response.status
    let message = HTTP_ERROR[code] || '连接异常，请稍后再试！'
    const err = new Error(message)
    err.code = code

    if (!error.response.config[HIDE_ERROR_PROP]) {
      if (!errorToatTimer) {
        Message({
          message: err.message,
          type: 'error',
          center: true,
          duration: 2000
        })
        errorToatTimer = setTimeout(() => {
          errorToatTimer && clearTimeout(errorToatTimer)
          errorToatTimer = null
        }, 2000)
      }
    }
    return Promise.reject(err)
  }
)

// 请求池
export const requestPool = new RequestPool()

/**
 * @description 请求包装体，所有的请求都应该调用该方法，因为封装了取消功能
 * @param {object} opts 详见axios的配置项
 * @returns
 */
function Request(opts) {
  const defaultOpts = {
    cancelable: true
  }
  opts = merge(defaultOpts, opts)
  const CancelToken = axios.CancelToken
  const source = CancelToken.source()
  const token = source.token
  let cancelable = opts.cancelable

  delete opts.cancelable

  const ajaxRequest = service(merge(opts, {
    cancelToken: token
  }))
    .then(res => {
      requestPool.remove(token)
      return res
    })
    .catch(function (thrown) {
      requestPool.remove(token)
      return Promise.reject(thrown)
    })

  ajaxRequest.cancel = function cancelRequest() {
    cancelable && source.cancel()
  }

  requestPool.add(token, ajaxRequest)

  return ajaxRequest
}

const methods = [
  'get',
  'post',
  'put',
  'delete',
  'head',
  'options',
  'patch'
]

const hasDataMethod = [
  'post',
  'put',
  'patch'
]

methods.forEach(method => {
  Request[method] = function (...argument) {
    const hasData = hasDataMethod.includes(method)
    const opts = (hasData ? argument[2] : argument[1]) || {}

    return Request(merge(opts, {
      url: argument[0],
      method
    }, hasData ? {
      data: argument[1]
    } : {}))
  }
})

export default Request